package com.cmc.cloud.cmclink.doc.api.document;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.api.document.rep.*;
import com.cmc.cloud.cmclink.doc.api.document.req.AttachmentUpdateReqDto;
import com.cmc.cloud.cmclink.doc.api.document.req.RequirementsUpdateReqDto;
import com.cmc.cloud.cmclink.doc.api.document.req.so.*;
import com.cmc.cloud.cmclink.doc.api.document.req.soamendapplyvo.SoAmendApplyRespVO;
import com.cmc.cloud.cmclink.doc.constants.CommonConstants;
import com.cmc.cloud.cmclink.doc.convert.*;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.service.SpecialCargoApplyService;
import com.cmc.cloud.cmclink.doc.service.VoyageBsaService;
import com.cmc.cloud.cmclink.doc.service.impl.so.SoVslvoyServiceImpl;
import com.cmc.cloud.cmclink.doc.service.manifest.ManifestService;
import com.cmc.cloud.cmclink.doc.service.si.VoyageLockService;
import com.cmc.cloud.cmclink.doc.service.so.BizSoService;
import com.cmc.cloud.cmclink.doc.service.so.OperateLogService;
import com.cmc.cloud.cmclink.doc.service.so.SoAmendApplyService;
import com.cmc.cloud.cmclink.doc.service.so.SoService;
import com.cmc.cloud.cmclink.doc.service.special.SpcExternalApiService;
import com.cmc.cloud.cmclink.doc.util.CostChargeUtil;
import com.cmc.cloud.cmclink.doc.util.sepcial.CommonUtil;
import com.cmc.cloud.cmclink.doc.vo.sovo.attachmentvo.AttachmentUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.requirementsvo.RequirementsUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.socargovo.SoCargoSumVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.socargovo.SoCargoUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.socontactvo.SoContactUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.soforecastvo.SoForecastSumVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.soforecastvo.SoForecastUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.soscnvo.SoScnUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovo.*;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovslvoyvo.SoVslvoyUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.special.externalapivo.DgCreateOrUpdateVO;
import com.cmc.cloud.cmclink.doc.vo.special.externalapivo.FlexiCreateOrUpdateVO;
import com.cmc.cloud.cmclink.doc.vo.special.externalapivo.OdoCreateOrUpdateVO;
import com.cmc.cloud.cmclink.doc.vo.special.externalapivo.OogCreateOrUpdateVO;
import com.cmc.cloud.cmclink.doc.vo.specialcargoapplyvo.SpecialCargoVoyageUpdateReqVO;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.CommonResult;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cmes.framework.security.core.util.SecurityFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.Validator;
import java.util.*;
import java.util.stream.Collectors;

import static com.cmc.cloud.cmclink.doc.api.document.emums.ContentChangeStatusEnum.BOOKING_CHANGE_PROCESSING;
import static com.cmc.cloud.cmclink.doc.api.document.emums.ContentChangeStatusEnum.BOOKING_CHANGE_REJECTED;
import static com.cmc.cloud.cmclink.doc.api.document.emums.SoStatusEnum.*;
import static com.cmc.cloud.cmclink.doc.constants.errorconstant.SoErrorCodeConstants.SO_NOT_EXISTS;

/**
 * @author lcp
 * @date 2025/7/7
 */

@Slf4j
@RestController
@Valid
public class SoInfoApiImpl implements SoInfoApi {

    @Resource
    private SoMapper soMapper;

    @Resource
    private SoCargoMapper soCargoMapper;

    @Resource
    private SoForecastMapper soForecastMapper;

    @Resource
    private SoVslvoyMapper soVslvoyMapper;

    @Resource
    private BizSoService bizSoService;

    @Resource
    private SpecialCargoApplyService specialCargoApplyService;

    @Resource
    private Validator validator;

    @Resource
    private SoScnMapper soScnMapper;

    @Resource
    private SpcExternalApiService spcExternalApiService;

    @Resource
    private SoContactMapper soContactMapper;

    @Resource
    private AttachmentMapper attachmentMapper;

    @Resource
    private RequirementsMapper requirementsMapper;

    @Resource
    private SpecialCargoApplyMapper specialCargoApplyMapper;

    @Resource
    private SpecialCargoVoyageMapper specialCargoVoyageMapper;

    @Resource
    private SpecialCargoCntrMapper specialCargoCntrMapper;

    @Resource
    private SpecialCargoOogMapper specialCargoOogMapper;

    @Resource
    private SpecialCargoOdoMapper specialCargoOdoMapper;

    @Resource
    private SpecialCargoDgMapper specialCargoDgMapper;

    @Resource
    private SpecialCargoFlexiMapper specialCargoFlexiMapper;

    @Resource
    private SpecialCargoDgPackingMapper specialCargoDgPackingMapper;

    @Resource
    private SpecialCargoCasMapper specialCargoCasMapper;

    @Resource
    private BlnoMapper blnoMapper;

    @Resource
    private CargoCntrTypeRelationMapper cargoCntrTypeRelationMapper;

    @Resource
    VoyageLockService voyageLockService;

    @Resource
    ManifestService manifestService;

    @Resource
    SoAmendApplyService soAmendApplyService;

    @Resource
    private SoVslvoyServiceImpl soVslvoyService;

    @Resource
    private CostChargeUtil costChargeUtil;

    @Resource
    private SoService soService;


    /**
     * polEtd时海段的时离港时间
     *
     * @param soPageReqDto
     * @return
     */
    @Override
    public CommonResult<PageResult<SoRespDto>> querySoInfo(SoPageReqDto soPageReqDto) {
        IPage<SoDO> page = Page.of(soPageReqDto.getPageNo(), soPageReqDto.getPageSize());
        IPage<SoDO> pageResult = soMapper.rpcQuerySoInfo(page, soPageReqDto);
        List<SoRespDto> soRespDtoList = SoConvert.INSTANCE.convertSoRespDto(pageResult.getRecords());
        if (CollectionUtils.isEmpty(soRespDtoList)) {
            return CommonResult.success(new PageResult<>());
        }
        List<Long> soIds = soRespDtoList.stream().map(SoRespDto::getId).collect(Collectors.toList());
        //查询货物信息
        Map<Long, SoCargoSumVO> cargoSumMap = soCargoMapper.selectSumBySoIds(soIds).stream().collect(Collectors.toMap(SoCargoSumVO::getSoId, e -> e));
        //查询预配箱信息
        Map<Long, SoForecastSumVO> forecastSumMap = soForecastMapper.selectSumBySoIds(soIds).stream().collect(Collectors.toMap(SoForecastSumVO::getSoId, e -> e));
        //查询航线信息
        Map<Long, SoVslvoyDO> soVslvoyDoMap = soVslvoyMapper.seleceFirstLinerBySoIds(soIds, TransmodeEnum.LINER.getValue()).stream().collect(Collectors.toMap(SoVslvoyDO::getSoId, e -> e));
        //特种货主信息 网厅分页查询需要回显
        LambdaQueryWrapperX<SpecialCargoApplyDO> specialCargoApplyWrapperX = new LambdaQueryWrapperX<>();
        specialCargoApplyWrapperX.in(SpecialCargoApplyDO::getSoId, soIds);
        List<SpecialCargoApplyDO> specialCargoApplyList = specialCargoApplyMapper.selectList(specialCargoApplyWrapperX);
        Map<Long, SpecialCargoApplyDO> specialCargoApplyMap = com.cmes.framework.common.util.collection.CollectionUtils.convertMap(specialCargoApplyList, SpecialCargoApplyDO::getSoId);
        //集中处理返回的信息
        soRespDtoList.forEach(e -> {
            e.setSpecialCargoAuditStatus(Optional.ofNullable(specialCargoApplyMap.get(e.getId())).map(SpecialCargoApplyDO::getApplyStatus).orElse(null));
            SoCargoSumVO cargoSum = cargoSumMap.get(e.getId());
            if (cargoSum != null) {
                e.setCargoTypeSum(cargoSum.getCargoTypeSum());
                e.setSubCargoTypeSum(cargoSum.getSubCargoTypeSum());
                e.setGrossWeightSum(cargoSum.getGrossWeightSum());
            }
            SoForecastSumVO forecastSum = forecastSumMap.get(e.getId());
            if (forecastSum != null) {
                e.setForecastDesc(forecastSum.getForecastDesc());
            }
            SoVslvoyDO soVslvoyDo = soVslvoyDoMap.get(e.getId());
            if (soVslvoyDo != null) {
                e.setLaneCodeEx(soVslvoyDo.getLaneCodeEx());
            }
            checkCanAmend(e);
            checkCanCancelSo(e);
            setBlNoVisible(e);
        });
        PageResult<SoRespDto> soDoPageResult = new PageResult<>(soRespDtoList, pageResult.getTotal());
        return CommonResult.success(soDoPageResult);
    }

    /**
     * 网厅设置bl_no可见性,存在舱单or提单已确认才允许展示bl_no 兵哥说的
     * @param so so
     */
    private void setBlNoVisible(SoRespDto so){
        boolean showBlNoFlag = false;
        // 只影响网厅,不影响单证
        if(!Objects.equals(SoDataSourceEnum.WEB.getValue(), so.getDataSource())){
            return;
        }
        if (CollectionUtil.isNotEmpty(manifestService.findByBookingNos(Collections.singletonList(so.getBookingNo())))) {
           showBlNoFlag = true;
        }else{
            LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
            queryWrapperX.eqIfPresent(BlnoDO::getBookingNo, so.getBookingNo());
            queryWrapperX.in(BlnoDO::getSiStatus,
                    Arrays.asList(SiStatusEnum.BookingPendingSpace.getValue(),
                            SiStatusEnum.AwaitingMaterialUploaded.getValue(),
                            SiStatusEnum.BookingConfirmed.getValue(),
                            SiStatusEnum.BookingRejected.getValue()));
            Long count = blnoMapper.selectCount(queryWrapperX);
            if(count > 0){
                showBlNoFlag = true;
            }
        }
        if(!showBlNoFlag){
            so.setBlNo(null);
        }
    }

    /**
     * check是否可以取消SO,现在都可以取消,只在在调用取消接口的时候会check
     * @param so
     */
    private void checkCanCancelSo(SoRespDto so) {
        so.setCanCancelSo(true);
     /*   if(Arrays.asList(BookingProcessing.getValue(), BookingPendingSpace.getValue(), AwaitingMaterialUploaded.getValue()).contains(so.getSoStatus())){
            so.setCanCancelSo(true);
        }else if(Objects.equals(BookingCancelledByCustomer.getValue(), so.getSoStatus())){
            // 客户已经取消订舱就不能再取消了
            so.setCanCancelSo(false);
        }else if(Objects.equals(BookingConfirmed.getValue(), so.getSoStatus())){
            so.setCanCancelSo(so.getCanAmend());
        }*/
    }

    /**
     * 判断是否可以进行订舱修正,生成舱单,生成提单
     * @param so
     * @return 是否可以修正
     */
    private void checkCanAmend(SoRespDto so) {
        so.setCanAmend(true);
        so.setCanGenerateBl(true);
        so.setCanGenerateManifest(true);
        //1、当前 SO 状态=【订舱已确认】
        //2、当前 SO 订舱船名航次没有锁船
        //3、当前 SO 不存在预配舱单
        //4、当前 SO 不存在SI记录
        //5、当前 SO 不存在修改申请审批中记录状态是【订舱更改处理中】【订舱更改待补充材料】【客户取消订舱处理中】
        if (!Objects.equals(SoStatusEnum.BookingConfirmed.getValue(), so.getSoStatus())) {
            so.setCanGenerateBl(false);
            so.setCanGenerateManifest(false);
            so.setCanAmend(false);
            return;
        }
        List<SoVslvoyDO> soVslvoyList = soVslvoyService.seleteBySoId(so.getId());
        if (CollectionUtil.isNotEmpty(soVslvoyList)) {
            for (SoVslvoyDO soVslvoyDO : soVslvoyList) {
                if (TransmodeEnum.LINER.getValue().equals(soVslvoyDO.getTransmode())) {
                    //航次锁船记录
                    VoyageLockDO voyageLock = voyageLockService.findVoyageLock(soVslvoyDO.getVesselCode(), soVslvoyDO.getVoyageEx(), soVslvoyDO.getPolCode(), ExImTypeEnum.EX.getValue());
                    if (voyageLock != null && voyageLock.getVoyageLockFlag()) {
                        so.setCanGenerateBl(false);
                        so.setCanGenerateManifest(false);
                        so.setCanAmend(false);
                        return;
                    }
                }
            }
        }
        if (CollectionUtil.isNotEmpty(manifestService.findByBookingNos(Collections.singletonList(so.getBookingNo())))) {
            so.setCanGenerateManifest(false);
            so.setCanAmend(false);
        }
        LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.eq(BlnoDO::getBookingNo, so.getBookingNo());
        Long count = blnoMapper.selectCount(queryWrapperX);
        if (count > 0) {
            so.setCanGenerateBl(false);
            so.setCanAmend(false);
        }
        SoAmendApplyDO applyLast = soAmendApplyService.getByBookingNoNewest(so.getBookingNo());
        if(applyLast != null){
            // 订舱修正中和订舱修正拒绝才返回状态
            if(Arrays.asList(BOOKING_CHANGE_PROCESSING.getValue(), BOOKING_CHANGE_REJECTED.getValue()).contains(applyLast.getSoAmendStatus())){
                so.setSoAmendStatus(applyLast.getSoAmendStatus());
                SoAmendApplyRespVO soAmendApplyRespVO = SoAmendApplyConvert.INSTANCE.convert(applyLast);
                so.setSoAmendApplyRespVO(soAmendApplyRespVO);
            }
            if(Arrays.asList(ContentChangeStatusEnum.BOOKING_CHANGE_PROCESSING.getValue(), ContentChangeStatusEnum.BOOKING_CHANGE_AWAITING_MATERIAL_UPLOADED.getValue(), ContentChangeStatusEnum.BOOKING_CANCEL_PROCESSING.getValue()).contains(applyLast.getSoAmendStatus())){
                so.setCanGenerateBl(false);
                so.setCanGenerateManifest(false);
                so.setCanAmend(false);
            }
        }
    }

    @Override
    public CommonResult<SoForecastCountVO> querySoForecastCount(SoPageReqDto soPageReqDto) {
        List<SoDO> soDOList = soMapper.rpcQuerySoCntrInfo(soPageReqDto);
        List<Long> soIds = soDOList.stream().map(SoDO::getId).collect(Collectors.toList());
        SoForecastCountVO soForecastSumVO = new SoForecastCountVO();
        if (CollectionUtils.isNotEmpty(soIds)) {
            List<SoForecastBaseVO> soForecastBases = soForecastMapper.rpcQuerySoCntrInfo(soIds);
            if (CollectionUtils.isNotEmpty(soForecastBases)) {
                List<String> sizeTypeCount = new ArrayList<>();
                int teuNum = 0;
                for (SoForecastBaseVO soForecastBaseVO : soForecastBases) {
                    String sizeTypeNum = soForecastBaseVO.getCntrSize() + soForecastBaseVO.getCntrType() + "*" + soForecastBaseVO.getCntrQuantity();
                    if (CntrSizeEnum.size20.getValue().equals(soForecastBaseVO.getCntrSize())) {
                        teuNum = teuNum + soForecastBaseVO.getCntrQuantity().intValue();
                    } else {
                        teuNum = teuNum + soForecastBaseVO.getCntrQuantity().intValue() * 2;
                    }
                    sizeTypeCount.add(sizeTypeNum);
                }
                soForecastSumVO.setTeuNum(teuNum);
                soForecastSumVO.setSizeTypeNum(String.join(";", sizeTypeCount));
            }
        }
        return CommonResult.success(soForecastSumVO);
    }

    @Override
    public CommonResult<SoCreateAndUpdateReqDto> querySoDetailInfo(Long id) {
        if (id == null) {
            return CommonResult.success();
        }
        //查询SO基础信息
        SoDO soDo = soMapper.selectById(id);
        if (soDo == null || soDo.getId() == null) {
            return CommonResult.success();
        }
        SoCreateAndUpdateReqDto soCreateAndUpdateReqDto = new SoCreateAndUpdateReqDto();
        SoUpdateReqDto soUpdateReqDto = SoConvert.INSTANCE.convertSoUpdateReqDto(soDo);
        soCreateAndUpdateReqDto.setSo(soUpdateReqDto);
        //查询货物信息
        LambdaQueryWrapperX<SoCargoDO> soCargoDoLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        soCargoDoLambdaQueryWrapperX.eq(SoCargoDO::getSoId, id);
        List<SoCargoDO> soCargoDoList = soCargoMapper.selectList(soCargoDoLambdaQueryWrapperX);
        List<SoCargoUpdateReqDto> soCargoUpdateReqDtoList = SoCargoConvert.INSTANCE.convertSoCargoUpdateReqDtos(soCargoDoList);
        soCreateAndUpdateReqDto.setSoCargoList(soCargoUpdateReqDtoList);
        //查询预配箱信息
        LambdaQueryWrapperX<SoForecastDO> soForecastLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        soForecastLambdaQueryWrapperX.eq(SoForecastDO::getSoId, id);
        List<SoForecastDO> soForecastDoList = soForecastMapper.selectList(soForecastLambdaQueryWrapperX);
        List<SoForecastUpdateReqDto> soForecastUpdateReqDtoList = SoForecastConvert.INSTANCE.convertSoForecastUpdateReqDtos(soForecastDoList);
        soCreateAndUpdateReqDto.setSoForecastList(soForecastUpdateReqDtoList);
        //查询航线信息
        LambdaQueryWrapperX<SoVslvoyDO> soVslvoyLambdaQueryWrapperX = new LambdaQueryWrapperX<SoVslvoyDO>();
        soVslvoyLambdaQueryWrapperX.eq(SoVslvoyDO::getSoId, id);
        List<SoVslvoyDO> soVslvoyDoList = soVslvoyMapper.selectList(soVslvoyLambdaQueryWrapperX);
        List<SoVslvoyUpdateReqDto> soVslvoyUpdateReqDtoList = SoVslvoyConvert.INSTANCE.convertSoVslvoyUpdateReqDtos(soVslvoyDoList);
        setShippingScheduleTime(soVslvoyUpdateReqDtoList);
        soCreateAndUpdateReqDto.setSoVslvoyList(soVslvoyUpdateReqDtoList);
        //相关方信息
        LambdaQueryWrapperX<SoScnDO> soScnLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        soScnLambdaQueryWrapperX.eq(SoScnDO::getSoId, id);
        SoScnDO soScnDO = soScnMapper.selectOne(soScnLambdaQueryWrapperX);
        SoScnUpdateReqDto soScnUpdateReqDto = SoScnConvert.INSTANCE.convertSoScnUpdateReqDto(soScnDO);
        soCreateAndUpdateReqDto.setSoScn(soScnUpdateReqDto);
        //联系人
        LambdaQueryWrapperX<SoContactDO> soContactLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        soContactLambdaQueryWrapperX.eq(SoContactDO::getSoId, id);
        List<SoContactDO> soContactDoList = soContactMapper.selectList(soContactLambdaQueryWrapperX);
        List<SoContactUpdateReqDto> soContactUpdateReqDtoList = SoContactConvert.INSTANCE.convertSoContactUpdateReqDtos(soContactDoList);
        soCreateAndUpdateReqDto.setSoContactList(soContactUpdateReqDtoList);
        //附件信息
        LambdaQueryWrapperX<AttachmentDO> attachmentLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        attachmentLambdaQueryWrapperX.eq(AttachmentDO::getReferenceId, id).eq(AttachmentDO::getReferenceType, SoSiEnum.SO.getValue());
        List<AttachmentDO> attachmentDoList = attachmentMapper.selectList(attachmentLambdaQueryWrapperX);
        List<AttachmentUpdateReqDto> attachmentUpdateReqDtoList = AttachmentConvert.INSTANCE.convertAttachmentUpdateReqDtos(attachmentDoList);
        soCreateAndUpdateReqDto.setAttachmentList(attachmentUpdateReqDtoList);
        //附件要求
        LambdaQueryWrapperX<RequirementsDO> requirementsLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        requirementsLambdaQueryWrapperX.eq(RequirementsDO::getReferenceId, id).eq(RequirementsDO::getReferenceType, SoSiEnum.SO.getValue());
        List<RequirementsDO> requirementsDoList = requirementsMapper.selectList(requirementsLambdaQueryWrapperX);
        List<RequirementsUpdateReqDto> requirementsUpdateReqDtos = RequirementsConvert.INSTANCE.convertRequirementsUpdateReqDtos(requirementsDoList);
        soCreateAndUpdateReqDto.setRequirementsList(requirementsUpdateReqDtos);
        //特种货主信息
        LambdaQueryWrapperX<SpecialCargoApplyDO> specialCargoApplyWrapperX = new LambdaQueryWrapperX<>();
        specialCargoApplyWrapperX.eq(SpecialCargoApplyDO::getSoId, id);
        SpecialCargoApplyDO specialCargoApplyDo = specialCargoApplyMapper.selectOne(specialCargoApplyWrapperX);
        SpecialCargoApplyBaseApiVO specialCargoApplyBaseApiVo = SpecialCargoApplyConvert.INSTANCE.convertSpecialCargoApplyBaseApiVo(specialCargoApplyDo);
        soCreateAndUpdateReqDto.setSpcApplyInfo(specialCargoApplyBaseApiVo);
        // SO修正数据(取最新的那一条)
        SoAmendApplyDO soAmendApplyDO = soAmendApplyService.getByBookingNoNewest(soDo.getBookingNo());
        // SO修正数据拒绝理由填充
        if((soAmendApplyDO != null && Arrays.asList(BOOKING_CHANGE_PROCESSING.getValue(), BOOKING_CHANGE_REJECTED.getValue()).contains(soAmendApplyDO.getSoAmendStatus()))){
            // 订舱更正中和订舱更正拒绝才给前端
            SoAmendApplyRespVO soAmendApplyRespVO = SoAmendApplyConvert.INSTANCE.convert(soAmendApplyDO);
            soCreateAndUpdateReqDto.setSoAmendApplyRespVO(soAmendApplyRespVO);
        }
        if (specialCargoApplyDo != null && specialCargoApplyDo.getId() != null) {
            String applyType = specialCargoApplyDo.getApplyType();
            Long applyId = specialCargoApplyDo.getId();
            //特种货附件
            LambdaQueryWrapperX<AttachmentDO> specialAttachmentWrapperX = new LambdaQueryWrapperX<>();
            specialAttachmentWrapperX.eq(AttachmentDO::getReferenceId, applyId).eq(AttachmentDO::getReferenceType, SoSiEnum.SPC.getValue());
            List<AttachmentDO> specialAttachment = attachmentMapper.selectList(specialAttachmentWrapperX);
            List<AttachmentUpdateReqDto> spcAttachmentList = AttachmentConvert.INSTANCE.convertAttachmentUpdateReqDtos(specialAttachment);
            soCreateAndUpdateReqDto.setSpcAttachmentList(spcAttachmentList);
            //特种货船名航次信息查询
            LambdaQueryWrapperX<SpecialCargoVoyageDO> specialCargoVoyageWrapperX = new LambdaQueryWrapperX<>();
            specialCargoVoyageWrapperX.eq(SpecialCargoVoyageDO::getApplyId, applyId);
            List<SpecialCargoVoyageDO> specialCargoVoyageDoList = specialCargoVoyageMapper.selectList(specialCargoVoyageWrapperX);
            List<SpecialCargoVoyageBaseApiVO> spcVoyageInfo = SpecialCargoVoyageConvert.INSTANCE.convertSpecialCargoVoyageBaseApiVo(specialCargoVoyageDoList);
            soCreateAndUpdateReqDto.setSpcVoyageInfo(spcVoyageInfo);
            switch (SpecialEnum.valueOf(applyType)) {
                case ODO:
                    List<SpecialCargoCntrDO> specialCargoCntrOdoList = this.querySpecialCntrInfo(applyId);
                    List<CntrAndOdoBaseVO> odoCreateReqVO = new ArrayList<>();
                    if (CollectionUtils.isNotEmpty(specialCargoCntrOdoList)) {
                        List<Long> cntrIds = specialCargoCntrOdoList.stream().map(SpecialCargoCntrDO::getId).collect(Collectors.toList());
                        //ODO信息
                        LambdaQueryWrapperX<SpecialCargoOdoDO> specialCargoOdoWrapperX = new LambdaQueryWrapperX<>();
                        specialCargoOdoWrapperX.inIfPresent(SpecialCargoOdoDO::getCntrId, cntrIds);
                        List<SpecialCargoOdoDO> specialCargoOdoDoList = specialCargoOdoMapper.selectList(specialCargoOdoWrapperX);
                        //按照箱号分组
                        Map<Long, List<SpecialCargoOdoDO>> listMap = specialCargoOdoDoList.stream().collect(Collectors.groupingBy(SpecialCargoOdoDO::getCntrId));
                        specialCargoCntrOdoList.forEach(spec -> {
                            CntrAndOdoBaseVO cntrAndOdoBaseVO = new CntrAndOdoBaseVO();
                            cntrAndOdoBaseVO.setCntrBaseInfo(SpecialCargoCntrConvert.INSTANCE.convertSpecialCargoCntrBaseVo(spec));
                            if (listMap.containsKey(spec.getId())) {
                                cntrAndOdoBaseVO.setOdoBaseInfo(SpecialCargoOdoConvert.INSTANCE.convertSpecialCargoOdoBaseVO(listMap.get(spec.getId()).get(0)));
                            }
                            odoCreateReqVO.add(cntrAndOdoBaseVO);

                        });
                    }
                    soCreateAndUpdateReqDto.setOdoCreateReqVO(odoCreateReqVO);
                    break;
                case FLEXI:
                    List<SpecialCargoCntrDO> specialCargoCntrFlexiDoList = this.querySpecialCntrInfo(applyId);
                    List<CntrAndFlexiBaseVO> flexiCreateReqVO = new ArrayList<>();
                    if (CollectionUtils.isNotEmpty(specialCargoCntrFlexiDoList)) {
                        List<Long> cntrIds = specialCargoCntrFlexiDoList.stream().map(SpecialCargoCntrDO::getId).collect(Collectors.toList());
                        //Flexi信息
                        LambdaQueryWrapperX<SpecialCargoFlexiDO> specialCargoFlexiWrapperX = new LambdaQueryWrapperX<>();
                        specialCargoFlexiWrapperX.inIfPresent(SpecialCargoFlexiDO::getCntrId, cntrIds);
                        List<SpecialCargoFlexiDO> specialCargoFlexiDoList = specialCargoFlexiMapper.selectList(specialCargoFlexiWrapperX);
                        //按箱号分组
                        Map<Long, List<SpecialCargoFlexiDO>> listMap = specialCargoFlexiDoList.stream().collect(Collectors.groupingBy(SpecialCargoFlexiDO::getCntrId));
                        specialCargoCntrFlexiDoList.forEach(spc -> {
                            CntrAndFlexiBaseVO cntrAndFlexiBaseVO = new CntrAndFlexiBaseVO();
                            cntrAndFlexiBaseVO.setCntrBaseInfo(SpecialCargoCntrConvert.INSTANCE.convertSpecialCargoCntrBaseVo(spc));
                            if (listMap.containsKey(spc.getId())) {
                                cntrAndFlexiBaseVO.setFlexiBaseInfo(SpecialCargoFlexiConvert.INSTANCE.convertSpecialCargoFlexiBaseVo(listMap.get(spc.getId()).get(0)));
                            }
                            flexiCreateReqVO.add(cntrAndFlexiBaseVO);
                        });
                    }
                    soCreateAndUpdateReqDto.setFlexiCreateReqVO(flexiCreateReqVO);
                    break;
                case DG:
                    CntrAndDgBaseVO dgCreateReqVo = new CntrAndDgBaseVO();
                    //箱信息
                    List<SpecialCargoCntrDO> cntrDos = this.querySpecialCntrInfo(applyId);
                    List<SpecialCargoDgDO> dgDOList = specialCargoDgMapper.selectList(new LambdaQueryWrapper<SpecialCargoDgDO>().eq(SpecialCargoDgDO::getApplyId, applyId));

                    if (CollectionUtil.isNotEmpty(cntrDos)) {
                        List<SpecialCargoCntrBaseVO> cntrBaseVOList = new ArrayList<>();
                        for (SpecialCargoCntrDO cntr : cntrDos) {
                            SpecialCargoCntrBaseVO cntrBaseVO = SpecialCargoCntrConvert.INSTANCE.convertSpecialCargoCntrBaseVo(cntr);
                            List<CargoCntrTypeRelationDO> cntrDgRelations = cargoCntrTypeRelationMapper.selectList(new LambdaQueryWrapperX<CargoCntrTypeRelationDO>()
                                    .eq(CargoCntrTypeRelationDO::getApplyId, cntr.getApplyId()).eq(CargoCntrTypeRelationDO::getCntrId, cntr.getId()));
                            if (CollectionUtil.isNotEmpty(cntrDgRelations)) {
                                List<Long> dgIds = cntrDgRelations.stream().map(CargoCntrTypeRelationDO::getDgId).collect(Collectors.toList());
                                List<SpecialCargoDgDO> resultList = dgDOList.stream().filter(dgDO -> dgIds.contains(dgDO.getId())).collect(Collectors.toList());
                                List<String> unNo = resultList.stream().map(SpecialCargoDgDO::getUnNo).collect(Collectors.toList());
                                List<String> imoClass = resultList.stream().map(SpecialCargoDgDO::getImoClass).collect(Collectors.toList());
                                cntrBaseVO.setUnNo(unNo);
                                cntrBaseVO.setImoClass(imoClass);
                                cntrBaseVO.setDgId(dgIds);
                                cntrBaseVO.setDgLabels(dgIds);
                            }
                            cntrBaseVOList.add(cntrBaseVO);
                        }
                        dgCreateReqVo.setCntrBaseInfo(cntrBaseVOList);
                    }
                    //DG信息
                    List<SpecialCargoDgBaseVO> specialCargoDgBaseVos = SpecialCargoDgConvert.INSTANCE.convertSpecialCargoDgBaseVo(dgDOList);
                    if (CollectionUtils.isNotEmpty(specialCargoDgBaseVos)) {
                        specialCargoDgBaseVos.forEach(dgBase -> {
                            LambdaQueryWrapperX<SpecialCargoDgPackingDO> specialCargoDgPackingDoWrapperX = new LambdaQueryWrapperX<>();
                            specialCargoDgPackingDoWrapperX.eqIfPresent(SpecialCargoDgPackingDO::getDgId, dgBase.getId());
                            List<SpecialCargoDgPackingDO> specialCargoDgPackingDoList = specialCargoDgPackingMapper.selectList(specialCargoDgPackingDoWrapperX);
                            List<PackingCreateOrUpdateVO> dgPackingInfo = SpecialCargoDgPackingConvert.INSTANCE.convertPackingCreateOrUpdateVo(specialCargoDgPackingDoList);
                            Map<String, List<PackingCreateOrUpdateVO>> groupedByType = dgPackingInfo.stream()
                                    .collect(Collectors.groupingBy(PackingCreateOrUpdateVO::getPackingType));
                            dgBase.setInnerDgPacking(groupedByType.get(PackingEnum.INNER.getValue()));
                            dgBase.setOuterDgPacking(groupedByType.get(PackingEnum.OUTER.getValue()));
                            LambdaQueryWrapperX<SpecialCargoCasDO> specialCargoCasDoWrapperX = new LambdaQueryWrapperX<>();
                            specialCargoCasDoWrapperX.eqIfPresent(SpecialCargoCasDO::getSpecialCargoDgId, dgBase.getId());
                            List<SpecialCargoCasDO> specialCargoCasDoList = specialCargoCasMapper.selectList(specialCargoCasDoWrapperX);
                            List<CasCreateOrUpdateVO> dgCasInfo = SpecialCargoCasConvert.INSTANCE.convertCasCreateOrUpdateVo(specialCargoCasDoList);
                            dgBase.setDgCasInfo(dgCasInfo);
                        });
                        dgCreateReqVo.setDgBaseInfo(specialCargoDgBaseVos);
                    }
                    soCreateAndUpdateReqDto.setDgCreateReqVO(dgCreateReqVo);
                    break;
                default:
                    List<SpecialCargoCntrDO> specialCargoCntrDoList = this.querySpecialCntrInfo(applyId);
                    List<CntrAndOogBaseVO> oogCreateReqVO = new ArrayList<>();
                    if (CollectionUtils.isNotEmpty(specialCargoCntrDoList)) {
                        List<Long> cntrIds = specialCargoCntrDoList.stream().map(SpecialCargoCntrDO::getId).collect(Collectors.toList());
                        //OOG信息
                        LambdaQueryWrapperX<SpecialCargoOogDO> specialCargoOogWrapperX = new LambdaQueryWrapperX<>();
                        specialCargoOogWrapperX.inIfPresent(SpecialCargoOogDO::getCntrId, cntrIds);
                        List<SpecialCargoOogDO> specialCargoOdoDoList = specialCargoOogMapper.selectList(specialCargoOogWrapperX);
                        Map<Long, List<SpecialCargoOogDO>> listMap = specialCargoOdoDoList.stream().collect(Collectors.groupingBy(SpecialCargoOogDO::getCntrId));
                        specialCargoCntrDoList.forEach(spc -> {
                            CntrAndOogBaseVO cntrAndOogBaseVO = new CntrAndOogBaseVO();
                            cntrAndOogBaseVO.setCntrBaseInfo(SpecialCargoCntrConvert.INSTANCE.convertSpecialCargoCntrBaseVo(spc));
                            if (listMap.containsKey(spc.getId())) {
                                cntrAndOogBaseVO.setOogBaseInfo(SpecialCargoOogConvert.INSTANCE.convertSpecialCargoOogBaseVo(listMap.get(spc.getId()).get(0)));
                            }
                            oogCreateReqVO.add(cntrAndOogBaseVO);
                        });
                    }
                    soCreateAndUpdateReqDto.setOogCreateReqVO(oogCreateReqVO);
            }
        }
        return CommonResult.success(soCreateAndUpdateReqDto);
    }

    private void setShippingScheduleTime(List<SoVslvoyUpdateReqDto> soVslvoyDoList) {
        if (CollectionUtils.isEmpty(soVslvoyDoList)) {
            return;
        }
        for (SoVslvoyUpdateReqDto vslvoy : soVslvoyDoList) {
            String vesselCode = vslvoy.getVesselCode();
            if (StringUtils.isEmpty(vesselCode) || vesselCode.equalsIgnoreCase(CommonConstants.DEFAULT_VESSEL_CODE)) {
                continue;
            }
            List<String> vesselCodes = new ArrayList<>();
            vesselCodes.add(vesselCode);
            VesselVoyRespDTO voyExResp = new VesselVoyRespDTO();
            VesselVoyRespDTO voyImResp = new VesselVoyRespDTO();
            try {
                voyExResp = costChargeUtil.getVoyQueryRespDTO(vslvoy.getPolCode(), vslvoy.getVoyageIm(),
                        vslvoy.getLaneCodeEx(), vesselCodes);
            } catch (Exception e) {
                log.warn("get ex voy schedule exception", e);
            }

            try {
                voyImResp = costChargeUtil.getVoyQueryRespDTO(vslvoy.getPodCode(), vslvoy.getVoyageEx(),
                        vslvoy.getLaneCodeIm(), vesselCodes);
            } catch (Exception e) {
                log.warn("get ex voy schedule exception", e);
            }
            String polTime = voyExResp.getAtd();
            if (StringUtils.isEmpty(polTime)) {
                polTime = voyExResp.getEtd();
            }
            if (StringUtils.isEmpty(polTime)) {
                polTime = voyExResp.getPtd();
            }

            String podTime = voyImResp.getAta();
            if (StringUtils.isEmpty(podTime)) {
                podTime = voyImResp.getEta();
            }
            if (StringUtils.isEmpty(podTime)) {
                podTime = voyImResp.getPta();
            }
            vslvoy.setPolEtd(polTime);
            vslvoy.setPodEta(podTime);
        }
    }

    public List<SpecialCargoCntrDO> querySpecialCntrInfo(Long applyId) {
        //ODO数据查询货物信息查询
        LambdaQueryWrapperX<SpecialCargoCntrDO> specialCargoCntrWrapperX = new LambdaQueryWrapperX<>();
        specialCargoCntrWrapperX.eq(SpecialCargoCntrDO::getApplyId, applyId);
        List<SpecialCargoCntrDO> specialCargoCntrDoList = specialCargoCntrMapper.selectList(specialCargoCntrWrapperX);
        return specialCargoCntrDoList;
    }

    /**
     * 创建SO
     *
     * @param soCreateAndUpdateReqVo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<SoAndSpcRespDto> createDraft(SoCreateAndUpdateReqDto soCreateAndUpdateReqVo) {
        soCreateAndUpdateReqVo.setCommitOrSave(false);
        return createSo(soCreateAndUpdateReqVo);
    }

    /**
     * 创建SO
     *
     * @param soCreateAndUpdateReqVo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult<SoAndSpcRespDto> createSo(SoCreateAndUpdateReqDto soCreateAndUpdateReqVo) {
        //处理数据转换
        //SO基础信息
        SoUpdateReqVO soBaseInfo = SoConvert.INSTANCE.convetSoUpdateReqDto(soCreateAndUpdateReqVo.getSo());
        // 网厅特有逻辑 agreementNoInput有值就以agreementNoInput为协议号
        if(soCreateAndUpdateReqVo.getSo() != null && StringUtils.isNotBlank(soCreateAndUpdateReqVo.getSo().getAgreementNoInput())){
            soBaseInfo.setAgreementNo(soCreateAndUpdateReqVo.getSo().getAgreementNoInput());
        }
        //收发通信息
        SoScnUpdateReqVO soScn = SoScnConvert.INSTANCE.convertSoScnUpdateReqDto(soCreateAndUpdateReqVo.getSoScn());
        //船名航次信息
        List<SoVslvoyUpdateReqVO> soVslvoyList = SoVslvoyConvert.INSTANCE.convertSoVslvoyUpdateReqDto(soCreateAndUpdateReqVo.getSoVslvoyList());
        //预配箱信息
        List<SoForecastUpdateReqVO> soForecastList = SoForecastConvert.INSTANCE.convertSoForecastUpdateReqDto(soCreateAndUpdateReqVo.getSoForecastList());
        //货物信息
        List<SoCargoUpdateReqVO> soCargoList = SoCargoConvert.INSTANCE.convertSoCargoUpdateReqDto(soCreateAndUpdateReqVo.getSoCargoList());
        //订舱联系人信息
        List<SoContactUpdateReqVO> soContactList = SoContactConvert.INSTANCE.convertSoContactUpdateReqDto(soCreateAndUpdateReqVo.getSoContactList());
        //附加信息
        List<AttachmentUpdateReqVO> attachmentList = AttachmentConvert.INSTANCE.convertAttachmentUpdateReqDto(soCreateAndUpdateReqVo.getAttachmentList());
        //特种货附件
        List<AttachmentUpdateReqVO> spcAttachmentList = AttachmentConvert.INSTANCE.convertAttachmentUpdateReqDto(soCreateAndUpdateReqVo.getSpcAttachmentList());
        //订舱要求
        List<RequirementsUpdateReqVO> requirementsList = RequirementsConvert.INSTANCE.convertRequirementsUpdateReqDto(soCreateAndUpdateReqVo.getRequirementsList());
        //构建创建SO方法
        SoCreateAndUpdateReqVo createReqVO = new SoCreateAndUpdateReqVo();
        createReqVO.setSo(soBaseInfo);
        createReqVO.setSoScn(soScn);
        createReqVO.setSoVslvoyList(soVslvoyList);
        createReqVO.setSoForecastList(soForecastList);
        createReqVO.setSoCargoList(soCargoList);
        createReqVO.setSoContactList(soContactList);
        createReqVO.setAttachmentList(attachmentList);
        createReqVO.setRequirementsList(requirementsList);
        createReqVO.setCommitOrSave(soCreateAndUpdateReqVo.getCommitOrSave());
        Long soId = bizSoService.createOrUpdateSo(createReqVO, soCreateAndUpdateReqVo.getSo().getId() != null, soCreateAndUpdateReqVo.getSo().getCreator());
        if (soCreateAndUpdateReqVo.getCommitOrSave()) {
            bizSoService.batchSubmitDraft(Arrays.asList(soId), soCreateAndUpdateReqVo.getSo().getCreator());
        }
        SoDO soDO = soMapper.selectById(soId);
        SoAndSpcRespDto soAndSpcRespDto = new SoAndSpcRespDto();
        soAndSpcRespDto.setSoId(soId);

        String applyType = soCreateAndUpdateReqVo.getApplyType();
        if (StringUtils.isNotEmpty(applyType)) {
            SpecialCargoApplyBaseApiVO spcApplyInfo = soCreateAndUpdateReqVo.getSpcApplyInfo();
            spcApplyInfo.setBookingNo(soDO.getBookingNo());
            CommonUtil.validEmails(spcApplyInfo.getApplyEmail());
            Long applyId;
            List<SpecialCargoVoyageUpdateReqVO> spcVoyageInfo = SpecialCargoVoyageConvert.INSTANCE.convertApiList(soCreateAndUpdateReqVo.getSpcVoyageInfo());
            switch (SpecialEnum.valueOf(applyType)) {
                case ODO:
                    OdoCreateOrUpdateVO odo = SpecialCargoApplyConvert.INSTANCE.convertOdo(spcApplyInfo);
                    odo.setSoId(soId);
                    odo.setSpcVoyageInfo(spcVoyageInfo);
                    odo.setCntrAndOdoInfos(SpecialCargoOdoConvert.INSTANCE.convertApiList(soCreateAndUpdateReqVo.getOdoCreateReqVO()));
                    odo.setSpcAttachmentList(spcAttachmentList);
                    applyId = spcExternalApiService.createOrUpdateOdo(odo);
                    break;
                case FLEXI:
                    FlexiCreateOrUpdateVO flexi = SpecialCargoApplyConvert.INSTANCE.convertFlexi(spcApplyInfo);
                    flexi.setSoId(soId);
                    flexi.setSpcVoyageInfo(spcVoyageInfo);
                    flexi.setCntrAndFlexiInfos(SpecialCargoFlexiConvert.INSTANCE.convertApiList(soCreateAndUpdateReqVo.getFlexiCreateReqVO()));
                    flexi.setSpcAttachmentList(spcAttachmentList);
                    applyId = spcExternalApiService.createOrUpdateFlexi(flexi);
                    break;
                case DG:
                    DgCreateOrUpdateVO dg = SpecialCargoApplyConvert.INSTANCE.convertDg(spcApplyInfo);
                    dg.setSoId(soId);
                    dg.setSpcVoyageInfo(spcVoyageInfo);
                    com.cmc.cloud.cmclink.doc.vo.special.externalapivo.CntrAndDgBaseVO cntrAndDgBase = BeanUtil.copyProperties(soCreateAndUpdateReqVo.getDgCreateReqVO(), com.cmc.cloud.cmclink.doc.vo.special.externalapivo.CntrAndDgBaseVO.class);
                    dg.setCntrAndDgInfos(cntrAndDgBase);
                    dg.setSpcAttachmentList(spcAttachmentList);
                    applyId = spcExternalApiService.createOrUpdateDg(dg);
                    break;
                default:
                    OogCreateOrUpdateVO oog = SpecialCargoApplyConvert.INSTANCE.convertOog(spcApplyInfo);
                    oog.setSoId(soId);
                    oog.setSpcVoyageInfo(spcVoyageInfo);
                    oog.setCntrAndOogInfos(SpecialCargoOogConvert.INSTANCE.convertApiList(soCreateAndUpdateReqVo.getOogCreateReqVO()));
                    oog.setSpcAttachmentList(spcAttachmentList);
                    applyId = spcExternalApiService.createOrUpdateOog(oog);
            }
            if (soCreateAndUpdateReqVo.getCommitOrSave()) {
                specialCargoApplyService.createApproval(applyId, soCreateAndUpdateReqVo.getSpcApplyInfo().getCreator(),false);
            }
            soAndSpcRespDto.setSpcId(applyId);
        }
        return CommonResult.success(soAndSpcRespDto);
    }

    @Override
    public CommonResult<FileUploadRespDto> bookingComfirmPrint(Long soId) {
        FileUploadRespVo fileUploadRespVo = bizSoService.bookingComfirmPrintAddress(soId);
        FileUploadRespDto fileUploadRespDto = SoConvert.INSTANCE.convertFileUploadRespDto(fileUploadRespVo);
        return CommonResult.success(fileUploadRespDto);
    }
    @Override
    public CommonResult<BookingConfirmationDataDto> getBookingConfirmData(Long soId) {
        return CommonResult.success(bizSoService.getBookingConfirmData(soId));
    }

    @Override
    public CommonResult bookingComfirmPrint(List<Long> ids) {
        bizSoService.batchReturnToDraft(ids, String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        return CommonResult.success(true);
    }

    @Override
    public CommonResult batchDeleteDraft(List<Long> ids) {
        bizSoService.batchDeleteDraft(ids, String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        return CommonResult.success(true);
    }

    @Override
    public CommonResult batchApproveContent(List<Long> ids) {
        bizSoService.changeSoContentStatus(ids, null, null, SoContentStatusEnum.APPROVED.getValue(), String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        return CommonResult.success();
    }

    @Override
    public CommonResult<SoDetailCompareRespDto> getAmendApplyDetail(Long id) {
        SoDetailCompareRespVo amendApplyDetail = bizSoService.getAmendApplyDetail(id);
        SoDetailCompareRespDto attachmentUpdateReqDtoList = AttachmentConvert.INSTANCE.convertSoDetailCompareRespDto(amendApplyDetail);
        return CommonResult.success(attachmentUpdateReqDtoList);
    }

    @Override
    public CommonResult<SoDetailRespDto> getSo(Long id) {
        SoDetailRespVO soDetailRespVO = bizSoService.getDetail(id);
        SoDetailRespDto soDetailRespDto = AttachmentConvert.INSTANCE.convertSoDetailRespDto(soDetailRespVO);
        return CommonResult.success(soDetailRespDto);
    }

    @Override
    public CommonResult<SoDetailRespDto> getSoDetailList(BatchReturnToDraftDto reqVO) {
        SoDetailRespVO detail = bizSoService.getDetail(reqVO.getIds().get(0), reqVO.getUserId(), false);
        SoDetailRespDto soDetailRespDto = AttachmentConvert.INSTANCE.convertSoDetailRespDto(detail);
        return CommonResult.success(soDetailRespDto);
    }

    @Override
    public CommonResult batchReturnToDraft(BatchReturnToDraftDto batchReturnToDraftDto) {
        bizSoService.batchReturnToDraft(batchReturnToDraftDto.getIds(), batchReturnToDraftDto.getUserId());
        return CommonResult.success();
    }

    @Override
    public CommonResult<List<SoStatusCountVO>> countSoStatus(SoCountDTO soCountDTO) {
        return CommonResult.success(soMapper.countSoStatus(soCountDTO));
    }

    @Override
    public CommonResult<List<SoAndSpcRespDto>> queryNotOrderSi(String csmCode,String soStatus) {
        LambdaQueryWrapperX<SoDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.eqIfPresent(SoDO::getBookingParty,csmCode)
                .eq(SoDO::getSoStatus, soStatus)
                .eq(SoDO::getValidStatus,ValidStatusEnum.DEFAULT_VALID.getValue())
                .orderByDesc(SoDO::getCreateTime);
        List<SoDO> soDoList = soMapper.selectList(lambdaQueryWrapperX);
        if (CollectionUtils.isEmpty(soDoList)) {
            return CommonResult.success(new ArrayList<>());
        }
        //是否存在订舱更正或者船期修正审批中的状态
        List<SoDO> soList = soMapper.selectList(new LambdaQueryWrapperX<SoDO>()
                .inIfPresent(SoDO::getBookingNo, soDoList.stream().map(SoDO::getBookingNo).collect(Collectors.toList())).eqIfPresent(SoDO::getValidStatus, ValidStatusEnum.AMEND.getValue()));
        if (CollectionUtils.isNotEmpty(soList)) {
            List<Long> oldIds = soList.stream().map(SoDO::getOldId).collect(Collectors.toList());
            soDoList.removeIf(p -> oldIds.contains(p.getId()));
        }
        List<String> bookingNos = soDoList.stream().map(SoDO::getBookingNo).collect(Collectors.toList());
        LambdaQueryWrapperX<BlnoDO> blnoDoLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        blnoDoLambdaQueryWrapperX.inIfPresent(BlnoDO::getBookingNo, bookingNos).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue());
        List<BlnoDO> blnoDoList = blnoMapper.selectList(blnoDoLambdaQueryWrapperX);
        if (CollectionUtils.isNotEmpty(blnoDoList)) {
            List<String> existSi = blnoDoList.stream().map(BlnoDO::getBookingNo).collect(Collectors.toList());
            soDoList.removeIf(e -> existSi.contains(e.getBookingNo()));
        }
        List<SoAndSpcRespDto> soAndSpcRespDtos = soDoList.stream().map(entity -> {
            SoAndSpcRespDto dto = new SoAndSpcRespDto();
            dto.setSoId(entity.getId());
            dto.setBookingNo(entity.getBookingNo());
            dto.setBlNo(entity.getBlNo());
            return dto;
        }).collect(Collectors.toList());
        return CommonResult.success(soAndSpcRespDtos);
    }

    @Override
    public CommonResult<Void> batchDeleteDraft(String userId, Long soId) {
        bizSoService.batchDeleteDraft(Collections.singletonList(soId), userId);
        return CommonResult.success();
    }

    @Override
    public CommonResult<Void> batchCancelBooking(String userId, Long soId) {
        SoDO so = soService.getSo(soId);
        if(so == null){
            return CommonResult.success();
        }
        if(Objects.equals(BookingConfirmed.getValue(), so.getSoStatus())){
            // 订舱已确认取消订舱需要审批
            SoAmendApplyDO applyLast = soAmendApplyService.getByBookingNoNewest(so.getBookingNo());
            if(applyLast != null){
                if(Arrays.asList(ContentChangeStatusEnum.BOOKING_CHANGE_PROCESSING.getValue(), ContentChangeStatusEnum.BOOKING_CHANGE_AWAITING_MATERIAL_UPLOADED.getValue()).contains(applyLast.getSoAmendStatus())){
                    // 在途的订舱修正要撤回
                    bizSoService.amendCancel(applyLast.getId(), String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
                }
            }
            // 订舱已确认状态下取消订舱需要审批
            SoCancelReqVo reqVo = new SoCancelReqVo();
            reqVo.setSoId(soId);
            bizSoService.cancelSoAmend(reqVo, String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        }
        else if(Arrays.asList(AwaitingMaterialUploaded.getValue(), BookingPendingSpace.getValue(), BookingProcessing.getValue()).contains(so.getSoStatus())){
            // 无需审批直接取消订舱
            bizSoService.batchCancelBooking(Collections.singletonList(soId), userId);
        }
        return CommonResult.success();
    }

    @Override
    public CommonResult<SoRespDto> getSoDetailByBookingNo(String bookingNo) {
        SoDO soDO = soMapper.selectOne(SoDO::getBookingNo, bookingNo);
        if(Objects.isNull(soDO)){
            throw ServiceExceptionUtil.exception(SO_NOT_EXISTS);
        }
        return CommonResult.success(BeanUtil.copyProperties(soDO, SoRespDto.class));
    }

    @Override
    public CommonResult<List<SoBookingNoVO>> getSoBookingNoList(String bookingNo,String csmCode) {
        List<SoBookingNoVO> rtnList = new ArrayList<>();
        List<SoDO> soList = soMapper.selectList(new LambdaQueryWrapperX<SoDO>()
                        .eq(SoDO::getSoStatus, BookingConfirmed.getValue())
                        .eq(SoDO::getValidStatus,ValidStatusEnum.DEFAULT_VALID.getValue())
                        .eq(SoDO::getBookingParty, csmCode)
                        .like(SoDO::getBookingNo, bookingNo));
        soList.forEach(a->{
            rtnList.add(new SoBookingNoVO(a.getId(),a.getBookingNo(),new ArrayList<>()));
        });
        return CommonResult.success(rtnList);
    }

    @Override
    public CommonResult<Void> checkVslvoy(List<com.cmc.cloud.cmclink.doc.api.document.req.so.sovo.sovslvoyvo.SoVslvoyUpdateReqVO> soVslvoyList) {
        List<SoVslvoyUpdateReqVO> wrapList = BeanUtil.copyToList(soVslvoyList, SoVslvoyUpdateReqVO.class);
        bizSoService.checkVslvoy(wrapList);
        return CommonResult.success();
    }

}
